<?php
/**
 * SLS_BoListing Tool - Generate back-office listing of database entities with pagination, order, number results, filters features
 *  
 * @author Laurent Bientz 
 * @copyright SillySmart
 * @package SLS.Generics.Tools.SLS_Bo
 * @since 1.0 
 */
class SLS_BoListing
{		
	private $oController;
	private $model;
	private $db;
	private $clause = array();
	private $column;
	private $order;
	private $group = array();
	private $start = 0;
	private $length	= 20;
	private $total = 0;
	private $join = array();
	private $arrayFilters = array();
	private $arrayColumns = array();
	private $arrayModify = array();
	private $arrayDelete = array();
	private $arrayAdd = array();
	private $arrayClone = array();
	private $arrayEmail = array();
	private $urlForm;
	private $generic;
	
	/**
	 * Constructor
	 *
	 * @access public
	 * @param object $oController reference of the current running controller
	 * @param string $model the model you want to list
	 * @param string $alias the alias of the db
	 * @since 1.0
	 */
	public function __construct($oController,$model,$alias)
	{
		$this->oController = $oController;
		$this->model = $model;
		$this->db = $alias;
		$this->generic = SLS_Generic::getInstance();
	}
	
	/**
	 * Set the current action of the form
	 *
	 * @access public
	 * @param string $controller the current controller
	 * @param string $action the current action
	 * @since 1.0
	 */
	public function setCurrentUrl($controller,$action)
	{
		$this->urlForm = $this->generic->getFullPath($controller,$action);
	}
	
	/**
	 * Set the column and the order which be used by default list
	 *
	 * @access public
	 * @param string $column the column
	 * @param string $order the order
	 * @since 1.0
	 */
	public function setDefaultOrder($column,$order)
	{
		$this->column = $column;
		$this->order = $order;
	}
	
	/**
	 * Set the starting offset and the number of recordset you want to show used by default list
	 *
	 * @access public
	 * @param int $start the starting offset
	 * @param int $length the number of recordset
	 * @since 1.0
	 */
	public function setDefaultLimit($start,$length)
	{
		$this->start = $start;
		$this->length = $length;
	}
	
	/**
	 * Set the default clause for the list
	 *
	 * @access public
	 * @param array $clause the wanted clause
	 * <code>
	 * array
	 * (
	 * 		[0] => array
	 * 			(
	 *				["column"]	= "user_login"
	 *				["value"]	= "admin"
	 *				["mode"]	= "like"|"notlike"|"beginwith"|"endwith"|"equal"|"notequal"|"lt"|"le"|"ge"|"gt"|"null"|"notnull"|"in"
	 *				)
	 *		[1] => array
	 *			(
	 *				["column"]	= "user_department"
	 *				["value"]	= "75"
	 *				["mode"]	= "like"|"notlike"|"beginwith"|"endwith"|"equal"|"notequal"|"lt"|"le"|"ge"|"gt"|"null"|"notnull"|"in"
	 *			)
	 * )
	 * </code>
	 * @since 1.0
	 */
	public function setDefaultClause($clause)
	{
		$this->clause = $clause;
	}
	
	/**
	 * Set the allowed filters for user
	 *
	 * @access public
	 * @param array $columns the mysql columns and their descriptions
	 * <code>
	 * array
	 * (
	 *		[0] => array
	 *				(
	 *					["column"]			= "mysql_column1"
	 *					["desccription"]	= "My column 1"	 
	 *				)
	 *		[1] => array
	 *				(
	 *					["column"]			= "mysql_column2"
	 *					["desccription"]	= "My column 2"	 
	 *				)
	 * )
	 * </code>
	 * @since 1.0
	 */
	public function setAllowedFilters($columns=array())
	{
		$this->arrayFilters = $columns;
	}
	
	/**
	 * Set the columns you want to display into the list
	 *
	 * @access public
	 * @param array $columns the mysql columns and their descriptions
	 * <code>
	 * array
	 * (
	 *		[0] => array
	 *				(
	 *					["column"]			= "mysql_column1"
	 *					["desccription"]	= "My column 1"	 
	 *				)
	 *		[1] => array
	 *				(
	 *					["column"]			= "mysql_column2"
	 *					["desccription"]	= "My column 2"	 
	 *				)
	 * )
	 * </code>
	 * @since 1.0
	 */
	public function setColumnsToShow($columns=array())
	{
		$this->arrayColumns = $columns;
	}
	
	/**
	 * Set the group by statement
	 *
	 * @access public
	 * @param array $group <code>array("column_1","...","column_n")</code>
	 * @since 1.0
	 */
	public function setDefaultGroupBy($group)
	{
		$this->group = $group;
	}
	
	/**
	 * Map a modification action to the list
	 *
	 * @access public
	 * @param string $controller generic name of the controller
	 * @param string $action generic name of the action
	 * @param string $paramName name of the dynamic param for modification (default: id)
	 * @since 1.0
	 */
	public function addActionModify($controller,$action,$paramName="id")
	{
		$urlArray = $this->generic->getTranslatedController($controller,$action);
		$this->arrayModify = array("mod" => $urlArray["controller"],
								   "smod" => $urlArray["scontroller"],
								   "param"=>$paramName);		
	}
	
	/**
	 * Map a delete action to the list
	 *
	 * @access public
	 * @param string $controller generic name of the controller
	 * @param string $action generic name of the action
	 * @param string $paramName name of the dynamic param for delete (default: id)
	 * @since 1.0
	 */
	public function addActionDelete($controller,$action,$paramName="id")
	{
		$urlArray = $this->generic->getTranslatedController($controller,$action);
		$this->arrayDelete = array("mod" => $urlArray["controller"],
								   "smod" => $urlArray["scontroller"],
								   "param"=>$paramName);
	}
	
	/**
	 * Map a add action to the list
	 *
	 * @access public
	 * @param string $controller generic name of the controller
	 * @param string $action generic name of the action
	 * @since 1.0
	 */
	public function addActionAdd($controller,$action)
	{
		$urlArray = $this->generic->getTranslatedController($controller,$action);
		$this->arrayAdd = array("mod" => $urlArray["controller"],
								"smod" => $urlArray["scontroller"]);
	}
	
	/**
	 * Map a clone action to the list
	 *
	 * @access public
	 * @param string $controller generic name of the controller
	 * @param string $action generic name of the action
	 * @since 1.0
	 */
	public function addActionClone($controller,$action)
	{
		$urlArray = $this->generic->getTranslatedController($controller,$action);
		$this->arrayClone = array("mod" => $urlArray["controller"],
								  "smod" => $urlArray["scontroller"]);
	}
	
	/**
	 * Map a email action to the list
	 *
	 * @access public
	 * @param string $controller generic name of the controller
	 * @param string $action generic name of the action
	 * @since 1.0.8
	 */
	public function addActionEmail($controller,$action)
	{
		$urlArray = $this->generic->getTranslatedController($controller,$action);
		$this->arrayEmail = array("mod" => $urlArray["controller"],
								  "smod" => $urlArray["scontroller"]);
	}
	
	/**
	 * Add a natural join clause to the current listing
	 *
	 * @access public
	 * @param array $join the tables to join
	 * @since 1.0
	 */
	public function setJoin($join)
	{
		$this->join = $join;
	}
	
	/**
	 * Construct the list
	 *
	 * @access public
	 * @since 1.0
	 */
	public function constructList()
	{
		$xmlType = new SLS_XMLToolbox(file_get_contents($this->generic->getPathConfig("configSls")."/types.xml"));
		$xmlFilter = new SLS_XMLToolbox(file_get_contents($this->generic->getPathConfig("configSls")."/filters.xml"));
		
		// If form reloading
		if ($this->generic->getObjectHttpRequest()->getParam("reload") == "true")
		{
			// Pagination + Order
			$this->column 	= $this->generic->getObjectHttpRequest()->getParam("column");
			$this->order	= $this->generic->getObjectHttpRequest()->getParam("order");
			$this->start	= ($this->generic->getObjectHttpRequest()->getParam("start") == "")  ? $this->start  : $this->generic->getObjectHttpRequest()->getParam("start");
			$this->length	= ($this->generic->getObjectHttpRequest()->getParam("length") == "") ? $this->length : $this->generic->getObjectHttpRequest()->getParam("length");
			
			// Filters
			for($i=0 ; $i<$count=count($this->arrayFilters) ; $i++)
			{
				if ($this->generic->getObjectHttpRequest()->getParam("filter_exact_".($i+1)) == "null" || 
					$this->generic->getObjectHttpRequest()->getParam("filter_exact_".($i+1)) == "notnull" ||
					$this->generic->getObjectHttpRequest()->getParam("filter_value_".($i+1)) != "")
				{
					$arrayTmp = array("column" 	=> $this->generic->getObjectHttpRequest()->getParam("filter_name_".($i+1)),
									  "value"	=> ($this->generic->getObjectHttpRequest()->getParam("filter_exact_".($i+1)) == "in") ? explode(",",$this->generic->getObjectHttpRequest()->getParam("filter_value_".($i+1))) : $this->generic->getObjectHttpRequest()->getParam("filter_value_".($i+1)),
									  "mode"	=> $this->generic->getObjectHttpRequest()->getParam("filter_exact_".($i+1)));
					array_push($this->clause,$arrayTmp);
				}
			}
						
			// Group by
			$group_filter = $this->generic->getObjectHttpRequest()->getParam("group_filter");
			
			if (!empty($group_filter))
				$this->setDefaultGroupBy($group_filter);
		}
		
		// XML's results
		if ($this->generic->useModel(SLS_String::tableToClass($this->model),$this->db))
		{			
			$class = ucfirst($this->db)."_".SLS_String::tableToClass($this->model);
			$object = new $class();
			
			if (!empty($this->column) && !empty($this->order))
				$order = array(0=>array("column"=>$this->column,"order"=>$this->order));
			else
				$order = array(0=>array("column"=>$object->getPrimaryKey(),"order"=>"DESC"));
			if (empty($this->clause) && $object->isMultilanguage())
				$this->clause = array(array("column"=>"pk_lang","value"=>$this->generic->getSiteConfig("defaultLang"),"mode"=>"equal"));
			
			$entities = $object->searchModels("",$this->join,$this->clause,$this->group,$order,array("start"=>$this->start,"length"=>$this->length));
			$this->total = $object->countModels("",$this->join,$this->clause,$this->group);
			
			$xmlToolBox = new SLS_XMLToolbox(false);
			$xmlToolBox->startTag("entities");
			for ($i=0 ; $i<$count=count($entities) ; $i++)
			{
				$xmlToolBox->startTag("entity");
				foreach ($entities[$i] as $key => $value)
				{
					$tables = array($object->getTable());
					foreach($this->join as $join)
						array_push($tables,(is_array($join)) ? $join["table"] : $join);
					
					foreach($tables as $cur_table)
					{
						$found = false;
						
						// Search possible types
						$result = array_shift($xmlType->getTagsAttribute("//sls_configs/entry[@table='".$object->getDatabase()."_".$cur_table."' and @column='".$key."' and (@type = 'file_img' or @type = 'file_all' or @type='color' or @type = 'url')]","type"));
						if (!empty($result['attribute']) && !empty($value))
						{
							switch($result['attribute'])
							{
								case "url":
									$value = "<a href='".$value."' target='_blank' onclick='window.location = \"".$value."\"'>".$value."</a>";
									$found = true;
									break;
								case "file_all":
									if (file_exists($this->generic->getPathConfig("files").$value) && !is_dir($this->generic->getPathConfig("files").$value))
									{
										$value = "<a href='http://".$this->generic->getSiteConfig("domainName")."/".$this->generic->getPathConfig("files").$value."?".uniqid()."' target='_blank' onclick='window.location = \"http://".$this->generic->getSiteConfig("domainName")."/".$this->generic->getPathConfig("files").$value."?".uniqid()."\"'>".$value."</a>";
										$found = true;
									}
									else
										$value = "";
									break;
								case "file_img":
									if (file_exists($this->generic->getPathConfig("files").$value) && !is_dir($this->generic->getPathConfig("files").$value))
									{
										$value = "<a href='http://".$this->generic->getSiteConfig("domainName")."/".$this->generic->getPathConfig("files").$value."?".uniqid()."' target='_blank' onclick='window.location = \"http://".$this->generic->getSiteConfig("domainName")."/".$this->generic->getPathConfig("files").$value."?".uniqid()."\"'><img src='http://".$this->generic->getSiteConfig("domainName")."/".$this->generic->getPathConfig("files").$value."?".uniqid()."' alt='".$value."' style='max-width:80px;' /></a>";
										$found = true;
									}
									else
										$value = "";
									break;
								case "color":
									$rgb = SLS_String::hex2RGB($value);
									$value = "<div style='width:100%;color:".((((0.213 * $rgb["red"]) + (0.715 * $rgb["green"]) + (0.072 * $rgb["blue"])) < 0.5) ? "#FFF" : "#000").";background-color:#".$value."'>#".$value."<div>";
									$found = true;
									break;
							}
						}
						// Search password filter
						$result = array_shift($xmlFilter->getTags("//sls_configs/entry[@table='".$object->getDatabase()."_".$cur_table."' and @column='".$key."' and @filter='hash']/@hash"));
						if (!empty($result))
						{
							$value = "********";
							$found = true;
						}
						if ($found)
							break;
					}
					$xmlToolBox->addFullTag($key,$value,true);
				}
				$xmlToolBox->endTag("entity");
			}
			$xmlToolBox->endTag("entities");
			// /XML's results
			
			// Get all columns
			$columns = $object->getColumns();
			$cols = array($object->getTable() => array());
			foreach($columns as $column)			
				$cols[$object->getTable()][$column] = $column;
						
			foreach($this->join as $join)
			{
				$join = (is_array($join)) ? $join["table"] : $join;
				$classJoin = ucfirst($this->db)."_".SLS_String::tableToClass($join);
				$objectJoin = new $classJoin();
				$columnsJoin = $objectJoin->getColumns();
				$cols[$objectJoin->getTable()] = array();
				foreach($columnsJoin as $colJoin)
					if (!in_array($colJoin,$columns))
					{
						array_push($columns,$colJoin);						
						$cols[$objectJoin->getTable()][$colJoin] = $colJoin;
					}
			}
			// Gets columns comment
			foreach($cols as $table => $values)
			{
				$comments = SLS_Sql::getInstance()->showColumns($table);
				for($i=0 ; $i<$count=count($comments) ; $i++)				
					$cols[$table][$comments[$i]->Field] = "<span>".$object->getTableComment($table)."</span><br />".$comments[$i]->Comment;				
			}			
			// /Get all columns

			// XML's pagination + filters + group by + columns + order
			$xmlToolBox->startTag("page");
			$xmlToolBox->addFullTag("column",$this->column,true);
			$xmlToolBox->addFullTag("order",$this->order,true);
			$xmlToolBox->addFullTag("start",$this->start,true);
			$xmlToolBox->addFullTag("length",$this->length,true);
			$xmlToolBox->addFullTag("total",$this->total,true);			
			$xmlToolBox->startTag("filters");			
			for ($i=0 ; $i<$count=count($this->arrayFilters) ; $i++)
			{
				$description = "";
				
				foreach($cols as $table => $values)
				{
					if ($values[$this->arrayFilters[$i]["column"]] != "")
					{
						$description = $values[$this->arrayFilters[$i]["column"]];
						break;
					}
				}
				
				$xmlToolBox->startTag("filter");
				$xmlToolBox->addFullTag("name",$this->arrayFilters[$i]["column"],true);
				$xmlToolBox->addFullTag("desc",$description,true);
				$found = false;
				for($j=0 ; $j<$count2=count($this->arrayFilters) ; $j++)
				{
					if ($this->generic->getObjectHttpRequest()->getParam("filter_name_".($j+1)) == $this->arrayFilters[$i]["column"])
					{
						$xmlToolBox->addFullTag("value",$this->generic->getObjectHttpRequest()->getParam("filter_value_".($j+1)),true);
						$xmlToolBox->addFullTag("exact",$this->generic->getObjectHttpRequest()->getParam("filter_exact_".($j+1)),true);
						$found = true;
						break;
					}
					else if ($object->isMultilanguage() && $this->arrayFilters[$i]["column"] == "pk_lang")
					{
						$xmlToolBox->addFullTag("value",$this->generic->getSiteConfig("defaultLang"),true);
						$xmlToolBox->addFullTag("exact","true",true);
						$found = true;
						break;
					}
				}
				if (!$found)
					$xmlToolBox->addFullTag("value","",true);
				$xmlToolBox->endTag("filter");
			}
			$xmlToolBox->endTag("filters");
			$xmlToolBox->startTag("groups");
			
			foreach($cols as $table => $values)
			{
				foreach($values as $label => $comment)
				{
					if (is_array($this->group) && !empty($this->group))
						$selected = (in_array($label,$this->group)) ? "true" : "false";
					else 
						$selected = "false";
					$xmlToolBox->startTag("group");
					$xmlToolBox->addFullTag("name",$label,true);
					$xmlToolBox->addFullTag("desc",$comment,true);
					$xmlToolBox->addFullTag("selected",$selected,true);
					$xmlToolBox->endTag("group");
				}
			}
			$xmlToolBox->endTag("groups");
			$xmlToolBox->startTag("actions");
			if (!empty($this->arrayDelete))
			{
				$xmlToolBox->startTag("delete");
				$xmlToolBox->addFullTag("mod",$this->arrayDelete["mod"],true);
				$xmlToolBox->addFullTag("smod",$this->arrayDelete["smod"],true);
				$xmlToolBox->addFullTag("param",$this->arrayDelete["param"],true);
				$xmlToolBox->addFullTag("pk",$object->getPrimaryKey(),true);
				$xmlToolBox->endTag("delete");
			}
			if (!empty($this->arrayModify))
			{
				$xmlToolBox->startTag("modify");
				$xmlToolBox->addFullTag("mod",$this->arrayModify["mod"],true);
				$xmlToolBox->addFullTag("smod",$this->arrayModify["smod"],true);
				$xmlToolBox->addFullTag("param",$this->arrayModify["param"],true);
				$xmlToolBox->addFullTag("pk",$object->getPrimaryKey(),true);
				$xmlToolBox->endTag("modify");
			}
			if (!empty($this->arrayAdd))
			{
				$xmlToolBox->startTag("add");
				$xmlToolBox->addFullTag("mod",$this->arrayAdd["mod"],true);
				$xmlToolBox->addFullTag("smod",$this->arrayAdd["smod"],true);
				$xmlToolBox->endTag("add");
			}
			if (!empty($this->arrayClone))
			{
				$xmlToolBox->startTag("clone");
				$xmlToolBox->addFullTag("mod",$this->arrayClone["mod"],true);
				$xmlToolBox->addFullTag("smod",$this->arrayClone["smod"],true);
				$xmlToolBox->endTag("clone");
			}			
			$result = array_shift($xmlType->getTags("//sls_configs/entry[@table='".$object->getDatabase()."_".$cur_table."' and @type = 'email']/@column"));			
			if (!empty($this->arrayEmail) && !empty($result))
			{
				$xmlToolBox->startTag("email");
				$xmlToolBox->addFullTag("mod",$this->arrayEmail["mod"],true);
				$xmlToolBox->addFullTag("smod",$this->arrayEmail["smod"],true);
				$xmlToolBox->endTag("email");
			}
			$xmlToolBox->endTag("actions");
			$xmlToolBox->startTag("columns");			
			for ($i=0 ; $i<$count=count($this->arrayColumns) ; $i++)
			{
				$description = "";
				
				foreach($cols as $table => $values)
				{
					if ($values[$this->arrayColumns[$i]["column"]] != "")
					{
						$description = $values[$this->arrayColumns[$i]["column"]];
						break;
					}
				}
				
				$xmlToolBox->startTag("column");
				$xmlToolBox->addFullTag("name",$this->arrayColumns[$i]["column"],true);
				$xmlToolBox->addFullTag("desc",$description,true);
				$xmlToolBox->endTag("column");
			}
			$description = $object->getTableComment($object->getTable(),$this->db);
			$xmlToolBox->endTag("columns");
			$xmlToolBox->addFullTag("actionForm",$this->urlForm,true);
			$xmlToolBox->addFullTag("model",$this->model,true);			
			$xmlToolBox->addFullTag("description",(empty($description)) ? ucfirst($object->getTable()) : $description,true);
			$xmlToolBox->addFullTag("pk",$object->getPrimaryKey(),true);
			$xmlToolBox->addFullTag("reload_add",($this->generic->getObjectHttpRequest()->getParam("SlsQuick") == 'ReloadAdd') ? "true" : "false",true);
			$xmlToolBox->addFullTag("reload_edit",($this->generic->getObjectHttpRequest()->getParam("SlsQuick") == 'ReloadEdit') ? "true" : "false",true);
			$xmlToolBox->addFullTag("reload_id",($this->generic->getObjectHttpRequest()->getParam("id") != "") ? $this->generic->getObjectHttpRequest()->getParam("id") : 0,true);
			$xmlToolBox->endTag("page");			
			// /XML's pagination
			
			$xmlTmp = $xmlToolBox->getXML();
			$xmlToolBox = new SLS_XMLToolbox($this->generic->getBufferXML());
			$xmlToolBox->appendXMLNode("//root",$xmlTmp);
			$this->oController->setXML($xmlToolBox->getXML());			
		}
		else
			SLS_Tracing::addTrace(new Exception("The model `".$this->model."` doestn't exist."),true);
	}
	
	/**
	 * Generic getter
	 *
	 * @access public
	 * @param string $name the key you want to get
	 * @return mixed the class variable wanted
	 * @since 1.0
	 */
	public function __get($name)
	{
		return (isset($this->{$name})) ? $this->{$name} : "";
	}
	
	/**
	 * Get fk for a column if exists
	 *
	 * @access public
	 * @param string $column the column to check
	 * @return array $fk the foreign key
	 * @since 1.0
	 */
	public function getFk($column)
	{
		$fk = array();
		$pathsHandle = file_get_contents($this->generic->getPathConfig("configSls")."/fks.xml");
		$xmlFk = new SLS_XMLToolbox($pathsHandle);
		$object = new $this->model();
		
		$res = $xmlFk->getTagsByAttributes("//sls_configs/entry",array("tableFk","columnFk"),array($object->getTable(),$column));
		
		if (!empty($res))
		{
			$fk["table"] = substr($res,(strpos($res,'tablePk="')+9),(strpos($res,'"/>')-(strpos($res,'tablePk="')+9)));
			$fk["label"] = substr($res,(strpos($res,'labelPk="')+9),(strpos($res,'" tablePk="')-(strpos($res,'labelPk="')+9)));
		}
		
		return $fk;
	}
}
?>